/**
 *
 */
package com.seteam.collabcrossword;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 * The Client handler class handles the clients that are connected to the
 * server. There will be one client handler for each client that connects to the
 * server.
 * 
 * @author SE Team
 */
public class ClientHandler extends Thread implements ThreadStopper {
	private Logger logger = LoggerFactory.getLogger(ClientHandler.class);

	// .
	private MessageParser messageParser;
	// Single Resource(containing the shared resource) parser shared by two
	// clients
	private Resources resources = null;
	private SynchronizedQueue<Object> transferQueue = null;
	private InterClientQueue<Object> interClientQueueClient1 = null;
	private InterClientQueue<Object> interClientQueueClient2 = null;

	// Sender and Receiver
	DataReceiver receiver = null;
	DataSender sender = null;

	// Client details
	private int shareId;

	// Stream for the socket
	private ObjectInputStream in = null;
	private ObjectOutputStream out = null;

	// Mutex so that sender and receiver are accessing in the right time.
	private Object mutex = new Object();

	/**
	 * Constructor for the Client Handler
	 * 
	 * @param socket
	 *            The socket object to which the clients have connected to.
	 * @param shareId
	 *            A unique Id that two clients who are playing together share.
	 * @param resources
	 *            The Resource object which contains the shared data between the
	 *            two clients.
	 * @param queue
	 *            Intra-Client queue, between the DataSender(data to the client
	 *            through socket output stream) and the DataReceiver(data from
	 *            the client through socket input stream)
	 * @param interClientQueueClient1
	 *            Queue between the DataSender of client 1 and the DataReciever
	 *            of client 2
	 * @param interClientQueueClient2
	 *            Queue between the DataSender of client 2 and the DataReciever
	 *            of client 1.
	 */
	public ClientHandler(Socket socket, int shareId, Resources resources,
			SynchronizedQueue<Object> queue,
			InterClientQueue<Object> interClientQueueClient1,
			InterClientQueue<Object> interClientQueueClient2) {
		this.shareId = shareId;
		this.resources = resources;
		this.messageParser = new MessageParser(this.resources);
		transferQueue = queue;
		this.interClientQueueClient1 = interClientQueueClient1;
		this.interClientQueueClient2 = interClientQueueClient2;
		try {
			// Create the input and output streams
			in = new ObjectInputStream(socket.getInputStream());
			out = new ObjectOutputStream(socket.getOutputStream());
		} catch (Exception e) {
			logger.error("Exception {}", e.getMessage());
		}

	}

	public int getShareId() {
		return this.shareId;
	}

	/**
	 * The run() method implements each request message that come to the server
	 * and performs the following functions: 1.Recieves the incoming request
	 * from the client 2.Creates the input stream, through which data is read by
	 * the server(Request Stream) 3.Create the output stream, though which data
	 * is written into the client(Response Stream) 4.Closes all the stream and
	 * socket objects created for the request.
	 */
	public void run() {
		try {
			// Create the receiver thread
			sender = new DataSender(out, transferQueue, mutex,
					interClientQueueClient2);

			receiver = new DataReceiver(in, messageParser, transferQueue,
					mutex, shareId, interClientQueueClient1);

			// Register the thread stopper interface
			receiver.registerThreadStopper(this);

			receiver.start();
			// Spin the sender thread
			sender.start();

		} catch (final Exception e) {
			logger.debug("Exception: {}", e.getMessage());
		}
	}

	/**
	 * Override the method of the interface. Here we stop the threads DataSender
	 * and DatReceiver of each client connected to the server.
	 */
	@SuppressWarnings("deprecation")
	public void stopThreads() {
		sender.stop();
		receiver.stop();
	}

}
